package main

import (
	"fmt"
	"os"
	"strconv"
	"flag"
	"log"
)

type CLI struct {}

func (cli *CLI) createBlockchain(address string){
	blockchain := CreateBlockchain(address)
	blockchain.db.Close()
	fmt.Println("Done!")
}
func (cli *CLI) getBalance(address string){
	blockchain := NewBlockchain(address)
	defer blockchain.db.Close()
	balance := 0
	UTXOs := blockchain.FindUTXO(address)
	for _,out := range UTXOs{
		balance += out.Value
	}
	fmt.Printf("Balance of '%s': %d\n", address, balance)
}
func (cli *CLI) printUsage(){
	fmt.Println("Usage:")
	fmt.Println("  getbalance -address ADDRESS - Get balance of ADDRESS")
	fmt.Println("  createblockchain -address ADDRESS - Create a blockchain and send genesis block reward to ADDRESS")
	fmt.Println("  printchain - Print all the blocks of the blockchain")
	fmt.Println("  send -from FROM -to TO -amount AMOUNT - Send AMOUNT of coins from FROM address to TO")
}
func (cli *CLI) validateArgs(){
	if len(os.Args)<2{
		cli.printUsage()
		os.Exit(1)
	}
}
func (cli *CLI) printChain(){
	blockchain := NewBlockchain("")
	defer blockchain.db.Close()
	blockchainiterator := blockchain.Iterator()
	for {
		block := blockchainiterator.Next()
		fmt.Printf("Prev hash: %x\n", block.PrevBlockHash)
		fmt.Printf("Hash: %x\n", block.Hash)
		pow := NewProofOfWork(block)
		fmt.Printf("PoW: %s\n", strconv.FormatBool(pow.Validate()))
		fmt.Println()
		if len(block.PrevBlockHash)==0{
			break
		}
	}
}
func (cli *CLI) send(from,to string,amount int){
	blockchain := NewBlockchain(from)
	defer blockchain.db.Close()
	tx := NewUTXOTransaction(from,to,amount,blockchain)
	blockchain.MineBlock([]*Transaction{tx})
	fmt.Println("Success!")
}
func (cli *CLI) Run(){
	cli.validateArgs()
	getBalanceCmd := flag.NewFlagSet("getbalance",flag.ExitOnError)
	createBlockchainCmd := flag.NewFlagSet("createblockchain",flag.ExitOnError)
	sendCmd := flag.NewFlagSet("send",flag.ExitOnError)
	printChainCmd := flag.NewFlagSet("printchain",flag.ExitOnError)
	getBalanceAddress := getBalanceCmd.String("address","","The address to get balance for")
	createBlockchainAddress := createBlockchainCmd.String("address","","The address to send genesis block reward to")
	sendFrom := sendCmd.String("from","","Source wallet address")
	sendTo := sendCmd.String("to","","Destination wallet address")
	sendAmount := sendCmd.Int("amount",0,"Amount to send")
	switch os.Args[1] {
	case "getbalance":
		err := getBalanceCmd.Parse(os.Args[2:])
		if err != nil {
			log.Panic(err)
		}
	case "createblockchain":
		err := createBlockchainCmd.Parse(os.Args[2:])
		if err != nil{
			log.Panic(err)
		}
	case "send":
		err := sendCmd.Parse(os.Args[2:])
		if err != nil {
			log.Panic(err)
		}
	case "printchain":
		err := printChainCmd.Parse(os.Args[2:])
		if err != nil {
			log.Panic(err)
		}
	default:
		cli.printUsage()
		os.Exit(1)
	}
	if getBalanceCmd.Parsed(){
		if *getBalanceAddress == ""{
			getBalanceCmd.Usage()
			os.Exit(1)
		}
		cli.getBalance(*getBalanceAddress)
	}
	if createBlockchainCmd.Parsed(){
		if *createBlockchainAddress==""{
			createBlockchainCmd.Usage()
			os.Exit(1)
		}
		cli.createBlockchain(*createBlockchainAddress)
	}
	if printChainCmd.Parsed(){
		cli.printChain()
	}
	if sendCmd.Parsed(){
		if *sendFrom=="" || *sendTo=="" || *sendAmount<=0{
			sendCmd.Usage()
			os.Exit(1)
		}
		cli.send(*sendFrom,*sendTo,*sendAmount)
	}
}